#include "dag.h"
#include "dag_types.h"

#include <string.h>
#include <iostream>

using namespace dag;
using namespace std;

Frame::Frame()
:	FrameSpec(0, 0, 0, 0),
	bools(NULL),
	ints(NULL),
	floats(NULL),
	doubles(NULL)
{
}

Frame::Frame(const FrameSpec& spec) 
:	FrameSpec(spec),
bools(NULL),
ints(NULL),
floats(NULL),
doubles(NULL)
{
	try {
		if (bSize > 0) {
			bools = new bool[bSize];
			memset(bools, 0, sizeof(bool) * bSize);
		}
		
		if (iSize > 0) {
			ints = new int[iSize];
			memset(ints, 0, sizeof(int) * iSize);
		}
		
		if (fSize > 0) {
			floats = new float[fSize];
			memset(floats, 0, sizeof(float) * fSize);
		}
		
		if (dSize > 0) {
			doubles = new double[dSize];
			memset(doubles, 0, sizeof(double) * dSize);
		}
	}
	catch (...) {
		delete[] bools;
		delete[] ints;
		delete[] floats;
		delete[] doubles;
		throw;
	}
}

Frame::Frame(size_t numBools, size_t numInts, size_t numFloats, size_t numDoubles)
:	FrameSpec(numBools, numInts, numFloats, numDoubles),
bools(NULL),
ints(NULL),
floats(NULL),
doubles(NULL)
{
	try {
		if (bSize > 0) {
			bools = new bool[bSize];
			memset(bools, 0, sizeof(bool) * bSize);
		}
		
		if (iSize > 0) {
			ints = new int[iSize];
			memset(ints, 0, sizeof(int) * iSize);
		}
		
		if (fSize > 0) {
			floats = new float[fSize];
			memset(floats, 0, sizeof(float) * fSize);
		}
		
		if (dSize > 0) {
			doubles = new double[dSize];
			memset(doubles, 0, sizeof(double) * dSize);
		}
	}
	catch (...) {
		delete[] bools;
		delete[] ints;
		delete[] floats;
		delete[] doubles;
		throw;
	}
}

Frame::Frame(const Frame& copy)
:	bools(NULL),
ints(NULL),
floats(NULL),
doubles(NULL)
{
	*this = copy;
}

Frame::~Frame() {
	delete[] bools;
	delete[] ints;
	delete[] floats;
	delete[] doubles;
}

Frame& Frame::operator=(const Frame& rhs) {
	
	if (this == &rhs) {
		return *this;
	}
	
	delete[] bools;
	bools = NULL;
	delete[] ints;
	ints = NULL;
	delete[] floats;
	floats = NULL;
	delete[] doubles;
	doubles = NULL;
	
	bSize = rhs.bSize;
	iSize = rhs.iSize;
	fSize = rhs.fSize;
	dSize = rhs.dSize;
	
	try {
		if (bSize > 0) {
			bools = new bool[bSize];
			memcpy(bools, rhs.bools, sizeof(bool) * bSize);
		}
		
		if (iSize > 0) {
			ints = new int[iSize];
			memcpy(ints, rhs.ints, sizeof(int) * iSize);
		}
		
		if (fSize > 0) {
			floats = new float[fSize];
			memcpy(floats, rhs.floats, sizeof(float) * fSize);
		}
		
		if (dSize > 0) {
			doubles = new double[dSize];
			memcpy(doubles, rhs.doubles, sizeof(double) * dSize);
		}
	}
	catch (...) {
		delete[] bools;
		bools = NULL;
		delete[] ints;
		ints = NULL;
		delete[] floats;
		floats = NULL;
		delete[] doubles;
		doubles = NULL;
		
		bSize = 0;
		iSize = 0;
		fSize = 0;
		dSize = 0;
		
		throw;
	}
	
	return *this;
}

void Frame::resize(const FrameSpec& spec) {
	delete[] bools;
	bools = NULL;
	delete[] ints;
	ints = NULL;
	delete[] floats;
	floats = NULL;
	delete[] doubles;
	bSize = 0;
	iSize = 0;
	fSize = 0;
	dSize = 0;
	
	try {
		if (spec.bSize > 0) {
			bools = new bool[spec.bSize];
		}
		if (spec.iSize > 0) {
			ints = new int[spec.iSize];
		}
		if (spec.fSize > 0) {
			floats = new float[spec.fSize];
		}
		if (spec.dSize > 0) {
			doubles = new double[spec.dSize];
		}
		static_cast<FrameSpec&>(*this) = spec;
	}
	catch (...) {
		delete[] bools;
		bools = NULL;
		delete[] ints;
		ints = NULL;
		delete[] floats;
		floats = NULL;
		delete[] doubles;
		doubles = NULL;
		static_cast<FrameSpec&>(*this) = FrameSpec(0,0,0,0);
		throw;
	}
}

void Frame::serialize(ostream& binaryStream) const {
	FrameSpec::serialize(binaryStream);
	binaryStream.write(dag_frame_file_token,
					   strlen(dag_frame_file_token));
	if (bSize > 0) {
		binaryStream.write((const char*)bools, bSize * sizeof(bool));
	}
	if (iSize > 0) {
		binaryStream.write((const char*)ints, iSize * sizeof(int));
	}
	if (fSize > 0) {
		binaryStream.write((const char*)floats, fSize * sizeof(float));
	}
	if (dSize > 0) {
		binaryStream.write((const char*)doubles, dSize * sizeof(double));
	}
}

auto_ptr<Frame> Frame::inflate(istream& binaryStream) {
	auto_ptr<Frame> frame(new Frame(FrameSpec()));
	frame->load(binaryStream);
	return frame;
}

void Frame::load(istream& binaryStream) {
	delete[] bools;
	delete[] ints;
	delete[] floats;
	delete[] doubles;
	
	bools = NULL;
	ints = NULL;
	floats = NULL;
	doubles = NULL;
	
	FrameSpec::load(binaryStream);
	char* header = NULL;
	
	try {
		/* check header in binary stream */
		size_t headerSize = strlen(dag_frame_file_token);
		header = new char[headerSize + 1];
		memset(header, 0, headerSize + 1);
		
		binaryStream.read(header, strlen(dag_frame_file_token));
		if (strcmp(header, dag_frame_file_token) != 0) {
			throw InvalidToken(header, 
							   dag_frame_file_token);
		}
		
		delete[] header;
		header = NULL;
		
		/* load values */
		if (bSize > 0) {
			bools = new bool[bSize];
			binaryStream.read((char*)&bools, bSize * sizeof(bool));
		}
		if (iSize > 0) {
			ints = new int[iSize];
			binaryStream.read((char*)&ints, iSize * sizeof(int));
		}
		if (fSize > 0) {
			floats = new float[fSize];
			binaryStream.read((char*)&floats, fSize * sizeof(float));
		}
		if (dSize > 0) {
			doubles = new double[dSize];
			binaryStream.read((char*)&doubles, dSize * sizeof(double));
		}
	}
	catch (...) {
		delete[] header;
		header = NULL;
		bSize = 0;
		delete[] bools;
		bools = NULL;
		iSize = 0;
		delete[] ints;
		ints = NULL;
		fSize = 0;
		delete[] floats;
		floats = NULL;
		dSize = 0;
		delete[] doubles;
		doubles = NULL;
		throw;
	}
}